﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Solidi.TestApp
{
	class Program
	{
		static void Main1(string[] args)
		{
			IDependencyBuilder builder = new DependencyBuilder();

			//builder.AddType<ChildService1>();
			//builder.AddType<Service1>().As<IService1>();

			builder.AddType<IService1>(c => new Service1(new ChildService1()));

			IDependencyContainer container = DependencyContainer.FromBuilder(builder);

			Stopwatch watch = new Stopwatch();

			watch.Start();

			for (int i = 0; i < 6000 * 200; i++)
			{
				IService1 service = container.Resolve<IService1>();

				container.Release(service);
			}

			watch.Stop();

			Console.WriteLine("took " + watch.ElapsedMilliseconds + " milliseconds");
			Console.Read();
		}

		static void Main(string[] args)
		{
			IComposer composer = new Composer3();

			Stopwatch watch = new Stopwatch();

			watch.Start();

			for (int i = 0; i < 6000 * 200; i++)
			{
				IService1 service = (IService1)composer.Compose(typeof(IService1));
			}

			watch.Stop();

			Console.WriteLine("took " + watch.ElapsedMilliseconds + " milliseconds");
			Console.Read();
		}

		interface IComposer
		{
			object Compose(Type type);
		}

		class Composer1 : IComposer
		{
			public object Compose(Type type)
			{
				if (type == typeof(IService1))
				{
					return new Service1((ChildService1)Compose(typeof(ChildService1)));
				}
				else if (type == typeof(ChildService1))
				{
					return new ChildService1();
				}
				else
				{
					throw new Exception("Cannot resolve " + type);
				}
			}
		}

		class Composer2 : IComposer
		{
			public object Compose(Type type)
			{
				if (type == typeof(IService1))
				{
					foreach (ConstructorInfo constructor in typeof(Service1).GetConstructors().OrderByDescending(ctor => ctor.GetParameters().Length))
					{
						ParameterInfo[] constructorParameters = constructor.GetParameters();

						object[] parameterInstances = new object[constructorParameters.Length];

						bool success = true;

						for (int i = 0; i < constructorParameters.Length; i++)
						{
							ParameterInfo constructorParameter = constructorParameters[i];

							parameterInstances[i] = Compose(constructorParameter.ParameterType);
						}

						if (success)
						{
							return constructor.Invoke(parameterInstances);
						}
					}

					throw new Exception("Cannot resolve " + type);
				}
				else if (type == typeof(ChildService1))
				{
					foreach (ConstructorInfo constructor in typeof(ChildService1).GetConstructors().OrderByDescending(ctor => ctor.GetParameters().Length))
					{
						ParameterInfo[] constructorParameters = constructor.GetParameters();

						object[] parameterInstances = new object[constructorParameters.Length];

						bool success = true;

						for (int i = 0; i < constructorParameters.Length; i++)
						{
							ParameterInfo constructorParameter = constructorParameters[i];

							parameterInstances[i] = Compose(constructorParameter.ParameterType);
						}

						if (success)
						{
							return constructor.Invoke(parameterInstances);
						}
					}

					throw new Exception("Cannot resolve " + type);
				}
				else
				{
					throw new Exception("Cannot resolve " + type);
				}
			}
		}

		class Composer3 : IComposer
		{
			//private ReflectorCtor[] ra1 = Reflector.Ctors(typeof(Service1));
			//private ReflectorCtor[] ra2 = Reflector.Ctors(typeof(ChildService1));

			public object Compose(Type type)
			{
				if (type == typeof(IService1))
				{
					foreach (ReflectorCtor constructor in Reflector.Ctors(typeof(Service1)))
					{
						ParameterInfo[] constructorParameters = constructor.Ctor.GetParameters();

						object[] parameterInstances = new object[constructorParameters.Length];

						bool success = true;

						for (int i = 0; i < constructorParameters.Length; i++)
						{
							ParameterInfo constructorParameter = constructorParameters[i];

							parameterInstances[i] = Compose(constructorParameter.ParameterType);
						}

						if (success)
						{
							return constructor.Instantiate(parameterInstances);
						}
					}

					throw new Exception("Cannot resolve " + type);
				}
				else if (type == typeof(ChildService1))
				{
					foreach (ReflectorCtor constructor in Reflector.Ctors(typeof(ChildService1)))
					{
						ParameterInfo[] constructorParameters = constructor.Ctor.GetParameters();

						object[] parameterInstances = new object[constructorParameters.Length];

						bool success = true;

						for (int i = 0; i < constructorParameters.Length; i++)
						{
							ParameterInfo constructorParameter = constructorParameters[i];

							parameterInstances[i] = Compose(constructorParameter.ParameterType);
						}

						if (success)
						{
							return constructor.Instantiate(parameterInstances);
						}
					}

					throw new Exception("Cannot resolve " + type);
				}
				else
				{
					throw new Exception("Cannot resolve " + type);
				}
			}
		}

		sealed class Reflector
		{
			private static ReaderWriterLockSlim @lock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
			private static readonly Dictionary<Type, ReflectorCtor[]> ctors =
				new Dictionary<Type, ReflectorCtor[]>();

			public static ReflectorCtor[] Ctors(Type type)
			{
				ReflectorCtor[] c;

				@lock.EnterReadLock();

				try
				{
					if (ctors.TryGetValue(type, out c))
					{
						return c;
					}
				}
				finally
				{
					@lock.ExitReadLock();
				}

				@lock.EnterWriteLock();

				try
				{
					if (!ctors.TryGetValue(type, out c))
					{
						ctors[type] = c = BuildCtors(type);
					}

					return c;
				}
				finally
				{
					@lock.ExitWriteLock();
				}
			}

			private static ReflectorCtor[] BuildCtors(Type type)
			{
				return type
					.GetConstructors()
					.OrderByDescending(c => c.GetParameters().Length)
					.Select(c => new ReflectorCtor(c))
					.ToArray();
			}
		}

		class ReflectorCtor
		{
			private Func<object[], object> _factory;
			private ConstructorInfo _ctor;

			public ReflectorCtor(ConstructorInfo ctor)
			{
				_ctor = ctor;
			}

			public ConstructorInfo Ctor
			{
				get
				{
					return _ctor;
				}
			}

			public object Instantiate(object[] parameters)
			{
				if (_factory == null)
				{
					Console.WriteLine("doh!");
					_factory = BuildFactory(_ctor);
				}

				return _factory(parameters);
			}

			private static Func<object[], object> BuildFactory(ConstructorInfo ctor)
			{
				var parameterInfos = ctor.GetParameters();
				var parameterExpressions = new Expression[parameterInfos.Length];
				var argument = Expression.Parameter(typeof(object[]), "parameters");
				for (var i = 0; i < parameterExpressions.Length; i++)
				{
					parameterExpressions[i] = Expression.Convert(
						Expression.ArrayIndex(argument, Expression.Constant(i, typeof(int))),
						parameterInfos[i].ParameterType.IsByRef ? parameterInfos[i].ParameterType.GetElementType() : parameterInfos[i].ParameterType);
				}
				return Expression.Lambda<Func<object[], object>>(
					Expression.New(ctor, parameterExpressions),
					new[] { argument }).Compile();
			}
		}

		/*
		public static object Compose(Type type)
		{
			if (type == typeof(IService1))
			{

			}

			foreach (ConstructorInfo constructor in type.GetConstructors().OrderByDescending(ctor => ctor.GetParameters().Length))
			{
				ParameterInfo[] constructorParameters = constructor.GetParameters();

				object[] parameterInstances = new object[constructorParameters.Length];

				bool success = true;

				for (int i = 0; i < constructorParameters.Length; i++)
				{
					ParameterInfo constructorParameter = constructorParameters[i];

					IDependencyParameter parameter = null;

					object parameterInstance;

					if (parameters != null)
					{
						parameter = parameters.FirstOrDefault(p => p.Match(constructorParameter));
					}

					if (parameter != null)
					{
						parameterInstances[i] = parameter.Factory(container);
					}
					else
					{
						if (container.Resolver.TryResolve(graphId, container, new DependencyKey(constructorParameter.ParameterType), out parameterInstance))
						{
							parameterInstances[i] = parameterInstance;
						}
						else
						{
							success = false;

							break;
						}
					}
				}

				if (success)
				{
					instance = constructor.Invoke(parameterInstances);

					return true;
				}
			}

			instance = null;

			return false;
		}
		*/

		interface IService1 : IDisposable
		{

		}

		class Service1 : IService1
		{
			public Service1(ChildService1 child)
			{

			}

			public void Dispose()
			{
				
			}
		}

		class ChildService1 : IDisposable
		{
			public ChildService1()
			{

			}

			public void Dispose()
			{

			}
		}
	}
}
